/*File contains the code for the connecting thread of the Beacon Nodes*/

#include "projectHeader.h"




void * beaconConnect(void * t)
{
	//pthread_mutex_lock(&connectMutex);
	//struct hostent *he;
	//struct hostent *heClient;
	//pthread_t readBeacon,writeBeacon;
	hostPort *peerInfo;
	peerInfo=(hostPort *)t;
	
	struct sockaddr_in peerAddr;
	int sockfd=0;
	

	//Main logic to connect to multiple beacon nodes and create network...

	//Creating socket.
	sockfd = socket(AF_INET, SOCK_STREAM,0);
	if(sockfd<0)
	{
		//printf("\nBEACON CONNECT :: Error while creating socket...\n");
		//pthread_mutex_unlock(&connectMutex);
		pthread_exit(NULL);
		exit(1);
	}
	//printf("\nBEACON CONNECT :: Sockfd = %d for server : %s:%s\n",sockfd,peerInfo->hostName,peerInfo->portNum);

	struct hostent *hePeerNeighbour;
	//struct hostent *heMyInfo;
	if ((hePeerNeighbour = gethostbyname(peerInfo->hostName)) == NULL) 
	{
		//printf("\nBEACON CONNECT :: Error resolving hostname..\n");
		//herror("error resolving hostname..");
		//pthread_mutex_unlock(&connectMutex);
		//pthread_exit(NULL);
	    //exit(1);
	}

	memset(&peerAddr,'\0', sizeof(peerAddr));
    peerAddr.sin_family = AF_INET;
    peerAddr.sin_addr.s_addr = inet_addr(inet_ntoa( *( struct in_addr*)( hePeerNeighbour -> h_addr_list[0]))); 
    peerAddr.sin_port = htons(atoi(peerInfo->portNum));

//	printf("\nBEACON CONNECT :: Server port %d and IP Address %s...\n",ntohs(peerAddr.sin_port),inet_ntoa( *( struct in_addr*)( hePeerNeighbour -> h_addr_list[0])));

	//Refer http://www.logix.cz/michal/devel/various/getaddrinfo.c.xp
	/*struct addrinfo hints, *servinfo=NULL, *p=NULL;
	int rv=0;*/

	struct addrinfo hints, *res, *p;
	int errcode;
	//char addrstr[100];
	//void *ptr;

	/*memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;*/
	
	memset (&hints, 0, sizeof (hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags |= AI_CANONNAME;

	/*if ((rv = getaddrinfo(peerInfo->hostName, peerInfo->portNum, &hints, &servinfo)) != 0)
	{
		//printf("I am exiting\n");
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		pthread_exit(NULL);
	}*/
	errcode = getaddrinfo (peerInfo->hostName, peerInfo->portNum, &hints, &res);
	if (errcode != 0)
    {
      //perror ("BEACON CONNECT :: getaddrinfo");
      exit(1);
    }
	char* connectedNodeListKey = (char*)malloc(strlen(peerInfo->hostName)+strlen(peerInfo->portNum)+2);
	memset(connectedNodeListKey,'\0',(strlen(connectedNodeListKey)));
	sprintf(connectedNodeListKey,"%s_%s",peerInfo->hostName,peerInfo->portNum);
	//connectedNodeListKey[strlen(peerInfo->hostName)+strlen(peerInfo->portNum)+!] = '\0';
	//printf("\nBEACON CONNECT :: connectedNodeListKey = %s\n",connectedNodeListKey);
	bool insertMapFlag = false;
	map<string,int>::iterator it;
	// loop through all the results and connect to the first we can
	while(1)
	{
		insertMapFlag = false;
		for(p = res; p != NULL; p = p->ai_next) 
		{
			insertMapFlag = false;
			if ((sockfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1) 
			{
				continue;
			}

			/*******************************************************************
			//Check if connection is already established or not.
			********************************************************************/
			pthread_mutex_lock(&connectedNodeListMutex);
			//printf("BEACON CONNECT :: Lock Acquired.\n");
			if(connectedNodeList.empty() || connectedNodeList.find(connectedNodeListKey) == connectedNodeList.end())
			{
				//printf("\nBEACON CONNECT :: Map is empty or key not found... So Connect...%d\n",sockfd);
				insertMapFlag = true;
				//printf("BEACON CONNECT :: Lock Released.\n");
				pthread_mutex_unlock(&connectedNodeListMutex);
				//connectedNodeList[connectedNodeListKey]=sockfd;
			}
			else
			{
				//insertMapFlag = false;
				//close(sockfd);
			//	printf("\nMap not empty...\n");
				pthread_mutex_unlock(&connectedNodeListMutex);
				//printf("BEACON CONNECT :: Releasing Lock1\n");
				break;
				
				/*if(connectedNodeList.find())
				{
				
				}*/
			}
			
			if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
			{
				close(sockfd);
				continue;
			}
			//pthread_mutex_unlock(&connectedNodeListMutex);
			//printf("BEACON CONNECT :: Releasing Lock2\n");
			break;
	    }
		if(insertMapFlag == false)
			break;
		if (p == NULL) 
		{
		
		}
		else 
		{
			//connect_success=1 ;/*it means connection succesfull*/
			break;
		}
		sleep(retry);
	}
	//inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),s, sizeof s);

	freeaddrinfo(res); // all done with this structure
	if(insertMapFlag == false)
	{
		close(sockfd);
		//pthread_mutex_lock(&printMutex);
		//printf("\nBEACON CONNECT :: Connection Already established... End Connect Thread\n\n");
	//	for ( it=connectedNodeList.begin() ; it != connectedNodeList.end(); it++ )
	//		cout << (*it).first << " => " << (*it).second << endl;
		//pthread_mutex_unlock(&printMutex);
		pthread_exit(NULL);
	}

	//printf("\n***************************BEACON CONNECT :: Connected***************************\n");
	//printf("\nConnected with my port no : %d\n",ntohs(myAddr.sin_port));
	//const char buffer[100] = "Hi, I am client";

//	send(sockfd,&buffer,sizeof(buffer),0);

	//Will send the Hello Header to Client now.
	//Populate Hello Data and Header Packets.

	helloMsgDataClass helloSendData;
	msgHeaderClass  helloSendHeader;

	
				
	helloSendData.HostPort=port;
	strncpy(helloSendData.Hostname,myHostName,strlen(myHostName));


	unsigned char *buff1;
	char* msg = (char*)"HLLO";

	//printf("BEACON CONNECT :: NodeID is %s\n",nodeInstanceId);
	helloSendHeader.messageType=0xFA;
	buff1=GetUOID((char *)nodeInstanceId,msg,buf, sizeof(buf));
	memcpy(helloSendHeader.UOID,buf,20);
	helloSendHeader.ttlInHeader=1;
	helloSendHeader.msgDataLength=sizeof(helloSendData.HostPort)+strlen(helloSendData.Hostname);
	


	if (send(sockfd,&helloSendHeader,sizeof(msgHeaderClass), 0) == -1)
	{
		//printf("BEACON CONNECT ERROR: error in sending Hello Header.\n ");
	}
	char helloDataSendBuf[512];
	memset(helloDataSendBuf,0,512);
	
	memcpy(helloDataSendBuf,&helloSendData.HostPort ,sizeof(helloSendData.HostPort));
	memcpy((helloDataSendBuf+sizeof(helloSendData.HostPort)),&helloSendData.Hostname ,strlen(helloSendData.Hostname));
	
	if (send(sockfd,helloDataSendBuf,helloSendHeader.msgDataLength, 0) == -1)
	{
		//printf("BEACON CONNECT ERROR: error in sending Hello Data.\n\n");
	}
	
	//pthread_mutex_lock(&printMutex);
	//printf("BEACON CONNECT :: SEND Connect:messageType: %u\n",helloSendHeader.messageType);
	//printf("BEACON CONNECT :: SEND Connect:UOID: ");
	//printUOID(helloSendHeader.UOID);
	//printUOID(buf);
	//printf("\nBEACON CONNECT :: SEND Connect:ttlInHeader: %u\n",helloSendHeader.ttlInHeader);
	//printf("BEACON CONNECT :: SEND Connect:HostPort: %d\n",helloSendData.HostPort);
	//printf("BEACON CONNECT :: SEND Connect:Hostname: %s\n",helloSendData.Hostname);
	//printf("\n\n");
	//pthread_mutex_unlock(&printMutex);

pthread_mutex_lock(&logFileMutex);
//
gettimeofday(&tv, NULL); 
int sec = tv.tv_sec ;
int msec = tv.tv_usec/1000;
char *time = (char *)malloc(14);
memset(time,0,14);
sprintf(time,"%10ld.%03d",(long)sec,msec);
time[strlen(time)]='\0';
fprintf(logFilePtr, "s  %s %s:%hu HLLO %d 0x%02x %x%x%x%x %hu %s\n",time,
	peerInfo->hostName,
	atoi(peerInfo->portNum),
	(helloSendHeader.msgDataLength+27),
	(unsigned char)helloSendHeader.ttlInHeader,
	(unsigned char)helloSendHeader.UOID[16],
	(unsigned char)helloSendHeader.UOID[17],
	(unsigned char)helloSendHeader.UOID[18],
	(unsigned char)helloSendHeader.UOID[19],
	(int)helloSendData.HostPort,helloSendData.Hostname);
pthread_mutex_unlock(&logFileMutex);








	//Wait in Recv for Hello Response from the peer.
	

			helloMsgDataClass  helloRecvData;
			msgHeaderClass  helloRecvHeader;
		
			int recv_ret=(int)recv(sockfd,&helloRecvHeader,sizeof(msgHeaderClass),0);
			//printf("\nBEACON CONNECT:::: After send hello Received bytes : %d\n",recv_ret);
			if(recv_ret == 0)
			{
				//printf("\nBEACON CONNECT :: After send Connection closed by other peer...\n");
				close(sockfd);
				//printf("\nBEACON CONNECT :: End Connect Thread\n\n");
				//pthread_mutex_unlock(&connectMutex);
				pthread_exit(NULL);	
			}
			else if(recv_ret<0)
			{
				//printf("BEACON CONNECT :: Error in receieveing Hello Header.\n");
				exit(1);
			}
			char helloDataRecvBuf[512];
			memset(helloDataRecvBuf,0,512);

			//recv hello data
			recv_ret=0;
			recv_ret=(int)recv(sockfd,&helloDataRecvBuf,helloRecvHeader.msgDataLength,0);
			if(recv<=0)
			{
				//printf("BEACON CONNECT :: ERROR: error in receiving hello data"); 
			}
			memcpy(&helloRecvData.HostPort,helloDataRecvBuf,sizeof(helloRecvData.HostPort));
			memcpy(helloRecvData.Hostname,helloDataRecvBuf+sizeof(helloRecvData.HostPort),helloRecvHeader.msgDataLength-sizeof(helloRecvData.HostPort));
		
			//pthread_mutex_lock(&printMutex);
			//printf("BEACON CONNECT :: RECV Connect:messageType: %u\n",helloRecvHeader.messageType);
			//printf("BEACON CONNECT :: RECV Connect:UOID: ");
			//printUOID(helloRecvHeader.UOID);
			//printf("\nBEACON CONNECT :: RECV Connect:ttlInHeader: %u\n",helloRecvHeader.ttlInHeader);
			//printf("BEACON CONNECT :: RECV Connect:HostPort: %d\n",helloRecvData.HostPort);
			//printf("BEACON CONNECT :: RECV Connect:Hostname: %s\n",helloRecvData.Hostname);
			//printf("\n\n");
		//	pthread_mutex_unlock(&printMutex);

pthread_mutex_lock(&logFileMutex);
//
gettimeofday(&tv, NULL); 
 sec = tv.tv_sec ;
msec = tv.tv_usec/1000;
time = (char *)malloc(14);
memset(time,0,14);
sprintf(time,"%10ld.%03d",(long)sec,msec);
time[strlen(time)]='\0';
fprintf(logFilePtr, "r  %s %s:%hu HLLO %d 0x%02x %x%x%x%x %hu %s\n",time,
	helloRecvData.Hostname,
	helloRecvData.HostPort,
	(helloRecvHeader.msgDataLength+27),
	(unsigned char)helloRecvHeader.ttlInHeader,
	(unsigned char)helloRecvHeader.UOID[16],
	(unsigned char)helloRecvHeader.UOID[17],
	(unsigned char)helloRecvHeader.UOID[18],
	(unsigned char)helloRecvHeader.UOID[19],
	helloRecvData.HostPort,helloRecvData.Hostname);
pthread_mutex_unlock(&logFileMutex);











		pthread_mutex_lock(&connectedNodeListMutex);
		if(connectedNodeList.empty() || connectedNodeList.find(connectedNodeListKey) == connectedNodeList.end())
		{
			//printf("\nBEACON CONNECT :: connectedNodeList Map is empty or key not found... So Connect...%d\n",sockfd);
			//insertMapFlag = true;
			connectedNodeList[connectedNodeListKey]=sockfd;
			//printf("\n%d ----> %d\n",connectedNodeList.find(connectedNodeListKey)->first,connectedNodeList.find(connectedNodeListKey)->second);
			//printf("\n%d ----> %d\n",connectedNodeList.find(connectedNodeListKey)->first,connectedNodeList.find(connectedNodeListKey)->second);
			//cout<<"BEACON CONNECT :: connectedNodeList map size = "<<connectedNodeList.size()<<endl;
			pthread_mutex_unlock(&connectedNodeListMutex);

			/************************************************************************************************
			*	Create Read and write thread to handle the request on that connection.
			*************************************************************************************************/
			//Find a index in bitset and add (sockfd,index) to map. USE PROPER MUTEX
			pthread_mutex_lock(&sockIndexMapMutex);
			pthread_mutex_lock(&wQBitsetMutex);
			
				int index= findBitIndex();

				
				
				if(index!= -1)
				{
					//printf("BEACON CONNECT :: SocketDesc: %d is mapped to %d.",sockfd,index);
					sockFDToQIndexMap.insert( pair<int,int>(sockfd,index));
					pthread_mutex_lock(&keepAliveMapMutex);
					keepAliveTimers.insert(pair<int,int>(index,keepAliveTimeOut));
					pthread_mutex_unlock(&keepAliveMapMutex);
				}
				else
				{
					//printf("BEACON CONNECT :: ERROR: MAX number of read-write Thread pairs .");
					pthread_exit(NULL);
				}
		
				pthread_create(&readBeacon[index], NULL,beaconRead,(void*)sockfd);
				pthread_create(&writeBeacon[index], NULL,beaconWrite,(void*)sockfd);	

			pthread_mutex_unlock(&wQBitsetMutex);
			pthread_mutex_unlock(&sockIndexMapMutex);
			pthread_join(readBeacon[index],NULL);
			pthread_join(writeBeacon[index],NULL);

			//pthread_create(&readBeacon, NULL,beaconRead,(void*)sockfd);
			//pthread_create(&writeBeacon, NULL,beaconWrite,(void*)sockfd);
			//pthread_join(readBeacon,NULL);
			//pthread_join(writeBeacon,NULL);
		}
		else
		{
			//printf("\nBEACON CONNECT :: Map is not empty... and map size is : %d\n",connectedNodeList.size());
			pthread_mutex_unlock(&connectedNodeListMutex);
			
		}


	close(sockfd);
//	printf("\nBEACON CONNECT :: End Connect Thread\n\n");
	//pthread_mutex_unlock(&connectMutex);
	pthread_exit(NULL);
	
}

